package vcp.server.requesthandler;

import java.io.Serializable;
import java.util.ArrayList;

import ocsf.server.ConnectionToClient;

import org.joda.time.Minutes;

import vcp.client.forms.SubscriptionCancelForm;
import vcp.client.request.CancelSubscriptionFormRequest;
import vcp.client.request.response.ResponseRequest;
import vcp.client.utility.Common.UserNotification;
import vcp.entity.Car;
import vcp.entity.Customer;
import vcp.entity.Lot;
import vcp.entity.Site;
import vcp.entity.Subscription;
import vcp.entity.SubscriptionHistory;
import vcp.entity.utility.RobotManager;
import vcp.enums.LotStatus;
import vcp.server.interfaces.IServer;
import vcp.server.utility.SystemClock;

/**
 * 
 * @author VCP team
 *         <p>
 *         this class handles the request to cancel subscription form on server
 *         side
 *         </p>
 */
public class CancelSubscriptionFormRequestHandler extends RequestHandler<CancelSubscriptionFormRequest>
{
	/**
	 * class constructor
	 */
	public CancelSubscriptionFormRequestHandler(IServer server)
	{
		super(server);
	}

	@Override
	public void execute(CancelSubscriptionFormRequest request, ConnectionToClient client)
	{
		ResponseRequest responseRequest = new ResponseRequest(request.getRequestID(), null);

		Subscription subscription = request.getForm().getSubscription();
		Customer customer = this.getDaoFactory().getCustomerDAO().findByID(subscription.getCustomerID());

		ArrayList<Serializable> serializableInfo = new ArrayList<>();
		serializableInfo.add(subscription);

		float refund = this.cancelSubscriptionAndRefund(request.getForm(), customer);

		int hoursDelta = SystemClock.getClockInstance().hoursBetweenCurrentHour(subscription.getArrivalDate());
		if ((hoursDelta >= 1) && (hoursDelta < 3))
		{
			refund *= 0.5;
		}
		else if (hoursDelta < 1)
		{
			refund = 0;
		}

		float customerRefundedBalanace = customer.getBalance() + refund;
		customer.setBalance(customerRefundedBalanace);
		this.getDaoFactory().getCustomerDAO().updateCustomerBalance(customer.getCustomerID(), customerRefundedBalanace);

		serializableInfo.add(new Float(customerRefundedBalanace));

		String customerRefundInfo = String.format(UserNotification.SUBSCRIPTION_REFUND__RefundValue__, refund);
		serializableInfo.add(customerRefundInfo);
		responseRequest.setData(serializableInfo);

		Site refereshedSite = this.getDaoFactory().getSiteDAO().findByID(subscription.getSite().getSiteID());
		this.getDaoFactory().getSiteDAO().getSiteLotsOnDemand(refereshedSite);

		this.sendResponseRefresh(refereshedSite, client);
		this.sendResponse(responseRequest, client);
	}

	/**
	 * method that cancels subscription and refunds the customer
	 * 
	 * @param subscriptionCancelForm
	 *            that was cancelled
	 * @param relevant
	 *            customer
	 * @param customerDAO
	 * @return amount of refund
	 */
	private float cancelSubscriptionAndRefund(SubscriptionCancelForm subscriptionCancelForm, Customer customer)
	{
		Subscription candidateForCancel = subscriptionCancelForm.getSubscription();
		Car car = this.getDaoFactory().getCarDAO().findByID(subscriptionCancelForm.getCarID());
		Lot lot = this.getDaoFactory().getLotDAO().findByReservedSubscriptionID(candidateForCancel.getSubscribtionID());
		Site site = lot.getParentSite();

		this.updateLotInSite(car, lot, site);

		this.updateSubscription(customer, candidateForCancel, car);

		int hoursDelta = (Minutes.minutesBetween(candidateForCancel.getArrivalDate(), candidateForCancel.getEstimatedLeaveDate()).getMinutes()) / 60;
		float refund = (hoursDelta * site.getOrderAheadPricePerHour());

		return refund;
	}

	/**
	 * update subscription to history or deletes car from subscription
	 * 
	 * @param customer
	 * @param candidateForCancel
	 * @param car
	 */
	private void updateSubscription(Customer customer, Subscription candidateForCancel, Car car)
	{
		SubscriptionHistory subscriptionHistory = this.getDaoFactory().getSubscriptionHistoryDAO().createFromSubscription(candidateForCancel);
		boolean subscriptionDeleted = this.getDaoFactory().getSubscriptionDAO().deleteByID(candidateForCancel.getSubscribtionID());
		if (subscriptionDeleted)
		{
			candidateForCancel.setDeleted(true);
			subscriptionHistory.setCanceld(true);
			this.getDaoFactory().getSubscriptionHistoryDAO().save(subscriptionHistory);

			int customerBussinessClass = customer.getBussinessClass() - 1;
			customer.setBussinessClass(customerBussinessClass);
			this.getDaoFactory().getCustomerDAO().updateBussinessClass(customer.getCustomerID(), customerBussinessClass);
		}
		else
		{
			int foundIndex = 0;
			for (int index = 0; index < candidateForCancel.getCars().size(); index++)
			{
				if (candidateForCancel.getCars().get(index).getCarID() == car.getCarID())
				{
					foundIndex = index;
					break;
				}
			}

			candidateForCancel.getCars().remove(foundIndex);
		}
	}

	/**
	 * clears site and lot info from DB
	 * 
	 * @param car
	 * @param lot
	 * @param site
	 */
	private void updateLotInSite(Car car, Lot lot, Site site)
	{
		int reservedSiteLots = site.getReservedLots();
		this.getDaoFactory().getSiteDAO().updateReservedLotsByID(site.getSiteID(), reservedSiteLots - 1);
		this.getDaoFactory().getLotDAO().updateCarInLot(site.getSiteID(), lot.getFloor(), lot.getRow(), lot.getColumn(), null);
		this.getDaoFactory().getLotDAO().updateLotStatusByID(site.getSiteID(), lot.getFloor(), lot.getRow(), lot.getColumn(), LotStatus.FREE);
		this.getDaoFactory().getLotDAO().updateReservedBySubscription(site.getSiteID(), lot.getFloor(), lot.getRow(), lot.getColumn(), null);

		this.getDaoFactory().getCarDAO().deleteByID(car.getCarID());
		RobotManager.getRobotManagerInstance().getSiteRobot(site.getSite()).removeCar();
	}
}
